// gcc -L. -o msDev msDev.c -lmodbus -lpthread -llua -lm -ldl
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <iconv.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "modbus.h"

int loopFlag = 0;
int devPort = 1502;
int numBits = 50000;
int numInputBits = 50000;
int numRegs = 50000;
int numInputRegs = 50000;
int debugFlag = 1;
int threadCount = 0;
int curFunction = 0;
int curAddress = 0;
int curNumber = 0;
pthread_mutex_t datamutex=PTHREAD_MUTEX_INITIALIZER;

modbus_t *ctx;
modbus_mapping_t *mb_mapping;

int get_bit(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	
	if(addr>mb_mapping->nb_bits || offset<0 || offset>7)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = (int)modbus_get_byte_from_bits(mb_mapping->tab_bits, addr, 8);
	int bit = (value >> offset) & 1;
	
	lua_pushinteger(L, bit);
	
	return 1;
}

int get_bits(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int num=luaL_checkinteger(L,3);
	
	if(addr>mb_mapping->nb_bits || offset<0 || offset>7 || num<=0 || offset+num>8)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = (int)modbus_get_byte_from_bits(mb_mapping->tab_bits, addr, 8);
	int bits = (value >> offset) & ((1 << num)-1);
	
	lua_pushinteger(L, bits);
	
	return 1;
}

int get_ibit(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	
	if(addr>mb_mapping->nb_input_bits || offset<0 || offset>7)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = (int)modbus_get_byte_from_bits(mb_mapping->tab_input_bits, addr, 8);
	int bit = (value >> offset) & 1;
	
	lua_pushinteger(L, bit);
	
	return 1;
}

int get_ibits(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int num=luaL_checkinteger(L,3);
	
	if(addr>mb_mapping->nb_input_bits || offset<0 || offset>7 || num<=0 || offset+num>8)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = (int)modbus_get_byte_from_bits(mb_mapping->tab_input_bits, addr, 8);
	int bits = (value >> offset) & ((1 << num)-1);
	
	lua_pushinteger(L, bits);
	
	return 1;
}

int get_reg(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	
	if(addr>mb_mapping->nb_registers)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int val = mb_mapping->tab_registers[addr];
	
	lua_pushinteger(L, val);
	
	return 1;
}

int get_reg_bit(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	
	if(addr>mb_mapping->nb_registers || offset<0 || offset>15)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = mb_mapping->tab_registers[addr];
	int bit = (value >> offset) & 1;
	
	lua_pushinteger(L, bit);
	
	return 1;
}

int get_reg_bits(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int num=luaL_checkinteger(L,3);
	
	if(addr>mb_mapping->nb_registers || offset<0 || offset>15 || num<=0 || offset+num>16)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = mb_mapping->tab_registers[addr];
	int bits = (value >> offset) & ((1 << num)-1);
	
	lua_pushinteger(L, bits);
	
	return 1;
}

int get_ireg(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	
	if(addr>mb_mapping->nb_input_registers)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = mb_mapping->tab_input_registers[addr];
	
	lua_pushinteger(L, value);
	return 1;
}

int get_ireg_bit(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	
	if(addr>mb_mapping->nb_input_registers || offset<0 || offset>15)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = mb_mapping->tab_input_registers[addr];
	
	int bit = (value >> offset) & 1;
	
	lua_pushinteger(L, bit);
	
	return 1;
}

int get_ireg_bits(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int num=luaL_checkinteger(L,3);
	
	if(addr>mb_mapping->nb_input_registers || offset<0 || offset>15 || num<=0 || offset+num>16)
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = mb_mapping->tab_input_registers[addr];
	
	int bits = (value >> offset) & ((1 << num)-1);
	
	lua_pushinteger(L, bits);
	
	return 1;
}

int set_bit(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int bit=luaL_checkinteger(L,3);
	
	if(addr>mb_mapping->nb_bits || offset<0 || offset>7 || (bit!=0 && bit!=1))
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = (int)modbus_get_byte_from_bits(mb_mapping->tab_bits, addr, 8);
	int mask=value;
	
	mask &= (1 << offset);
	mask |= (bit << offset);
	value |= mask;
	
	modbus_set_bits_from_byte(mb_mapping->tab_bits, addr, value);
	
	return 0;
}

int set_bits(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int num=luaL_checkinteger(L,3);
	int bits=luaL_checkinteger(L,4);
	
	if(addr>mb_mapping->nb_bits || offset<0 || offset>7 || num<=0 || offset+num>8 || bits<0 || bits>=(1 << num))
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = (int)modbus_get_byte_from_bits(mb_mapping->tab_bits, addr, 8);
	int mask=value;
	
	mask &= (((1 << num) - 1) << offset);
	mask |= (bits << offset);
	value |= mask;
	
	modbus_set_bits_from_byte(mb_mapping->tab_bits, addr, value);
	
	return 0;
}

int set_ibit(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int bit=luaL_checkinteger(L,3);
	
	if(addr>mb_mapping->nb_input_bits || offset<0 || offset>7 || (bit!=0 && bit!=1))
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = (int)modbus_get_byte_from_bits(mb_mapping->tab_input_bits, addr, 8);
	int mask=value;
	
	mask &= (1 << offset);
	mask |= (bit << offset);
	value |= mask;
	
	modbus_set_bits_from_byte(mb_mapping->tab_input_bits, addr, value);
	
	return 0;
}

int set_ibits(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int num=luaL_checkinteger(L,3);
	int bits=luaL_checkinteger(L,4);
	
	if(addr>mb_mapping->nb_input_bits || offset<0 || offset>7 || num<=0 || offset+num>8 || bits<0 || bits>=(1 << num))
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value = (int)modbus_get_byte_from_bits(mb_mapping->tab_input_bits, addr, 8);
	int mask=value;
	
	mask &= (((1 << num) - 1) << offset);
	mask |= (bits << offset);
	value |= mask;
	
	modbus_set_bits_from_byte(mb_mapping->tab_input_bits, addr, value);
	
	return 0;
}

int set_reg(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int value=luaL_checkinteger(L,2);
	
	if(addr>mb_mapping->nb_registers || value<0 || value>65535)
	{
		printf("parameter error\n");
		return 0;
	}
	
	mb_mapping->tab_registers[addr] = value;
	
	return 0;
}

int set_reg_bit(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int bit=luaL_checkinteger(L,3);
	
	if(addr>mb_mapping->nb_registers || offset<0 || offset>15 || (bit!=0 && bit!=1))
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value=mb_mapping->tab_registers[addr];
	int mask=value;
	
	mask &= (1 << offset);
	mask |= (bit << offset);
	value |= mask;
	
	mb_mapping->tab_registers[addr] = value;
	
	return 0;
}

int set_reg_bits(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int num=luaL_checkinteger(L,3);
	int bits=luaL_checkinteger(L,4);
	
	if(addr>mb_mapping->nb_registers || offset<0 || offset>15 || num<=0 || offset+num>16 || bits<0 || bits>=(1 << num))
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value=mb_mapping->tab_registers[addr];
	int mask=value;
	
	mask &= (((1 << num) - 1) << offset);
	mask |= (bits << offset);
	value |= mask;
	
	mb_mapping->tab_registers[addr] = value;
	
	return 0;
}

int set_ireg(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int value=luaL_checkinteger(L,2);
	
	if(addr>mb_mapping->nb_input_registers || value<0 || value>65535)
	{
		printf("parameter error\n");
		return 0;
	}
	
	mb_mapping->tab_input_registers[addr] = value;
	
	return 0;
}

int set_ireg_bit(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int bit=luaL_checkinteger(L,3);
	
	if(addr>mb_mapping->nb_input_registers || offset<0 || offset>15 || (bit!=0 && bit!=1))
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value=mb_mapping->tab_input_registers[addr];
	int mask=value;
	
	mask &= (1 << offset);
	mask |= (bit << offset);
	value |= mask;
	
	mb_mapping->tab_input_registers[addr] = value;
	
	return 0;
}

int set_ireg_bits(lua_State *L)
{
	int addr=luaL_checkinteger(L,1);
	int offset=luaL_checkinteger(L,2);
	int num=luaL_checkinteger(L,3);
	int bits=luaL_checkinteger(L,4);
	
	if(addr>mb_mapping->nb_input_registers || offset<0 || offset>15 || num<=0 || offset+num>16 || bits<0 || bits>=(1 << num))
	{
		printf("parameter error\n");
		return 0;
	}
	
	int value=mb_mapping->tab_input_registers[addr];
	int mask=value;
	
	mask &= (((1 << num) - 1) << offset);
	mask |= (bits << offset);
	value |= mask;
	
	mb_mapping->tab_input_registers[addr] = value;
	
	return 0;
}

int code_convert(lua_State *L)
{
	const char *fromCharset=luaL_checkstring(L,1);
	const char *toCharset=luaL_checkstring(L,2);
	const char *inStr=luaL_checkstring(L,3);
	size_t inLen=luaL_checkinteger(L,4);
	
	if(inLen>250)
	{
		printf("parameter error\n");
		return 0;
	}
	
	char outStr[1024];
	size_t outLen=1024;
	memset(outStr, 0, outLen);
	
	char *in=(char *)inStr;
	char *out=outStr;
	
	iconv_t cd=iconv_open(toCharset, fromCharset);
	
	iconv(cd, &in, &inLen, &out, &outLen);
	
	iconv_close(cd);

	lua_pushstring(L, outStr);
	
	return 1;
}

int setDebug(lua_State *L)
{
	debugFlag = luaL_checkinteger(L,1);
	
	if(debugFlag)
	{
		debugFlag = 1;
	}
	
	return 0;
}

int delaySim(lua_State *L)
{
	int delay=luaL_checkinteger(L,1);
	sleep(delay);
	return 0;
}

int exitLoop(lua_State *L)
{
	printf("exitLoop command,exit...\n");

	loopFlag = 0;
	
	return 0;
}

void pushFunction(lua_State *L)
{
	lua_pushcfunction(L,exitLoop);
	lua_setglobal(L,"exitLoop");
	lua_pushcfunction(L,delaySim);
	lua_setglobal(L,"delaySim");
	lua_pushcfunction(L,get_bit);
	lua_setglobal(L,"get_bit");
	lua_pushcfunction(L,get_bits);
	lua_setglobal(L,"get_bits");
	lua_pushcfunction(L,get_ibit);
	lua_setglobal(L,"get_ibit");
	lua_pushcfunction(L,get_ibits);
	lua_setglobal(L,"get_ibits");
	lua_pushcfunction(L,get_reg);
	lua_setglobal(L,"get_reg");
	lua_pushcfunction(L,get_reg_bit);
	lua_setglobal(L,"get_reg_bit");
	lua_pushcfunction(L,get_reg_bits);
	lua_setglobal(L,"get_reg_bits");
	lua_pushcfunction(L,get_ireg);
	lua_setglobal(L,"get_ireg");
	lua_pushcfunction(L,get_ireg_bit);
	lua_setglobal(L,"get_ireg_bit");
	lua_pushcfunction(L,get_ireg_bits);
	lua_setglobal(L,"get_ireg_bits");
	lua_pushcfunction(L,set_bit);
	lua_setglobal(L,"set_bit");
	lua_pushcfunction(L,set_bits);
	lua_setglobal(L,"set_bits");
	lua_pushcfunction(L,set_ibit);
	lua_setglobal(L,"set_ibit");
	lua_pushcfunction(L,set_ibits);
	lua_setglobal(L,"set_ibits");
	lua_pushcfunction(L,set_reg);
	lua_setglobal(L,"set_reg");
	lua_pushcfunction(L,set_reg_bit);
	lua_setglobal(L,"set_reg_bit");
	lua_pushcfunction(L,set_reg_bits);
	lua_setglobal(L,"set_reg_bits");
	lua_pushcfunction(L,set_ireg);
	lua_setglobal(L,"set_ireg");
	lua_pushcfunction(L,set_ireg_bit);
	lua_setglobal(L,"set_ireg_bit");
	lua_pushcfunction(L,set_ireg_bits);
	lua_setglobal(L,"set_ireg_bits");
	lua_pushcfunction(L,setDebug);
	lua_setglobal(L,"setDebug");
	lua_pushcfunction(L,code_convert);
	lua_setglobal(L,"code_convert");

}

void *thread_simdev(void *arg)
{
	printf("thread_simdev start\n");
	
	lua_State *L=luaL_newstate();
	luaL_openlibs(L);
	
	luaL_dofile(L,"./msDev.lua");
	
	pushFunction(L);
	
	lua_getglobal(L,"simDev");
	lua_pushinteger(L, curFunction);
	lua_pushinteger(L, curAddress);
	lua_pushinteger(L, curNumber);
	
	lua_pcall(L,3,0,0);
	
	lua_close(L);
	
	curFunction = 0;
	curAddress = 0;
	curNumber = 0;
	
	printf("thread_simdev exit\n");
}

void *thread_modbus(void *arg)
{
	threadCount++;
	
	printf("thread_modbus start..\n");
	
	int i;
	int rc;
	int ret;
	int func;
	int s = -1;
	
	uint8_t *query;
	int header_length;
	pthread_t t_simdev_id;
	
	while(loopFlag)
	{
		ctx = modbus_new_tcp("127.0.0.1", devPort);
		query = malloc(MODBUS_TCP_MAX_ADU_LENGTH);
		
		header_length = modbus_get_header_length(ctx);
		
		modbus_set_debug(ctx, debugFlag);
		
		if (mb_mapping == NULL) {
			printf("Failed to allocate the mapping\n");
			
			modbus_free(ctx);
			loopFlag = 0;
		}
		
		s = modbus_tcp_listen(ctx, 1);
		modbus_tcp_accept(ctx, &s);
		
		for (;;) {
			do {
				rc = modbus_receive(ctx, query);
			} while (rc == 0);
			
			if (rc == -1) {
				break;
			}
			
			func = query[header_length];
			if(func==5)
			{
				curFunction = func;
				curAddress = MODBUS_GET_INT16_FROM_INT8(query, header_length + 1);
				curNumber = 1;
				
				printf("command: func=%02x addr=%02x(%d) num=%d\n",
					curFunction, curAddress, curAddress, curNumber);
				
				ret = pthread_create(&t_simdev_id, NULL, thread_simdev, (void *)NULL);
			}
			if(func==6)
			{
				curFunction = func;
				curAddress = MODBUS_GET_INT16_FROM_INT8(query, header_length + 1);
				curNumber = 1;
				
				printf("command: func=%02x addr=%02x(%d) num=%d\n",
					curFunction, curAddress, curAddress, curNumber);
				
				ret = pthread_create(&t_simdev_id, NULL, thread_simdev, (void *)NULL);
			}
			if(func==15)
			{
				curFunction = func;
				curAddress = MODBUS_GET_INT16_FROM_INT8(query, header_length + 1);
				curNumber = MODBUS_GET_INT16_FROM_INT8(query, header_length + 3);
				
				printf("command: func=%02x addr=%02x(%d) num=%d\n",
					curFunction, curAddress, curAddress, curNumber);
				
				ret = pthread_create(&t_simdev_id, NULL, thread_simdev, (void *)NULL);
			}
			if(func==16)
			{
				curFunction = func;
				curAddress = MODBUS_GET_INT16_FROM_INT8(query, header_length + 1);
				curNumber = MODBUS_GET_INT16_FROM_INT8(query, header_length + 3);
				
				printf("command: func=%02x addr=%02x(%d) num=%d\n",
					curFunction, curAddress, curAddress, curNumber);
				
				ret = pthread_create(&t_simdev_id, NULL, thread_simdev, (void *)NULL);
			}
			
			rc = modbus_reply(ctx, query, rc, mb_mapping);
			if (rc == -1) {
				break;
			}
		}
		
		if (s != -1) {
			close(s);
		}
		
		free(query);
		
		modbus_close(ctx);
		modbus_free(ctx);
	}
	
	printf("thread_modbus end..\n");
	
	threadCount--;
}


void sig_int(int signo)
{
	printf("recv SIGINT signal\n");
	
	loopFlag = 0;
}

int main(int argc, char*argv[])
{
	printf("running...\n");
	
	signal(SIGINT, sig_int);
	signal(SIGPIPE, SIG_IGN);
	
	lua_State *L = luaL_newstate();
	luaL_openlibs(L);
	
	luaL_dofile(L, "./msDev.lua");
	
	pushFunction(L);
	
	lua_getglobal(L,"devPort");
	if(!lua_isnumber(L,-1))
	{
		printf("require integer argument devPort\n");
		lua_close(L);
		return -1;
	}
	devPort=lua_tointeger(L,-1);
	
	lua_getglobal(L,"numBits");
	if(!lua_isnumber(L,-1))
	{
		printf("require integer argument numBits\n");
		lua_close(L);
		return -1;
	}
	numBits=lua_tointeger(L,-1);
	
	lua_getglobal(L,"numInputBits");
	if(!lua_isnumber(L,-1))
	{
		printf("require integer argument numInputBits\n");
		lua_close(L);
		return -1;
	}
	numInputBits=lua_tointeger(L,-1);
	
	lua_getglobal(L,"numRegs");
	if(!lua_isnumber(L,-1))
	{
		printf("require integer argument numRegs\n");
		lua_close(L);
		return -1;
	}
	numRegs=lua_tointeger(L,-1);
	
	lua_getglobal(L,"numInputRegs");
	if(!lua_isnumber(L,-1))
	{
		printf("require integer argument numInputRegs\n");
		lua_close(L);
		return -1;
	}
	numInputRegs=lua_tointeger(L,-1);
	
	lua_getglobal(L,"debugFlag");
	if(!lua_isnumber(L,-1))
	{
		printf("require integer argument debugFlag\n");
		lua_close(L);
		return -1;
	}
	debugFlag=lua_tointeger(L,-1);

	mb_mapping = modbus_mapping_new(numBits, numInputBits, numRegs, numInputRegs);
	
	lua_getglobal(L,"initDev");
	
	lua_pcall(L,0,0,0);
	
	loopFlag = 1;
	
	int ret;
	pthread_t t_modbus_id;
	ret = pthread_create(&t_modbus_id, NULL, thread_modbus, (void *)NULL);
	
	fd_set rd;
	struct timeval tv;
	char buff[256];
	
	while(loopFlag)
	{
		FD_ZERO(&rd);
		FD_SET(0, &rd);
		
		tv.tv_sec = 5;
		tv.tv_usec = 0;
		ret = select(1, &rd, NULL, NULL, &tv);
		
		if(ret==-1) break;
		else if(ret==0) continue;
		
		fscanf(stdin, "%s", buff);
		
		ret = luaL_loadbuffer(L, buff, strlen(buff), "line") || 
			lua_pcall(L, 0, 0, 0);
		if(ret)
		{
			printf("%s\n", lua_tostring(L,-1));
			lua_pop(L, 1);
		}
		modbus_set_debug(ctx, debugFlag);
	}
	
	lua_close(L);
	
	modbus_mapping_free(mb_mapping);
	
	while(threadCount>0)
	{
		if(threadCount==1)
		{
			threadCount = 0;
			pthread_cancel(t_modbus_id);
		}
		sleep(1);
	}
	
	printf("exit...\n");
	
	return 0;
}
